
use std::io::Result;


trait CryptoDevice {
    fn is_open(&self) -> bool;
    fn open(&mut self) -> Result<()>;
    fn close(&mut self) -> Result<()>;
    fn encrypt(&self, key: &[u8], data: &[u8]) -> Result<Vec<u8>>;
}

trait Crypto<D: CryptoDevice>
{
    fn device(&self) -> &D;
    fn device_mut(&mut self) -> &mut D;

    fn encrypt(&mut self, key: &[u8], data: &[u8]) -> Result<Vec<u8>>{
        println!("Crypto::encrypt");
        self.device().encrypt(key, data)
    }
}

#[derive(Default)]
struct XYDDevice {
    opened: bool
}
impl CryptoDevice for XYDDevice {
    fn is_open(&self) -> bool {
        self.opened
    }
    fn open(&mut self) -> Result<()> {
        println!("XYDDevice::open");
        if !self.opened {
            self.opened = true;
        }
        Ok(())
    }
    fn close(&mut self) -> Result<()> {
        println!("XYDDevice::close");
        if self.opened {
            self.opened = false;
        }
        Ok(())
    }
    fn encrypt(&self, key:&[u8], data: &[u8]) -> Result<Vec<u8>>{
        println!("XYDDevice::encrypt::key:{:?}::data:{:?}", key, data);
        let mut v = Vec::from(key);
        v.extend(data);
        Ok(v)
    }
}

struct SimpleCrypto<D: CryptoDevice> 
{
    device : D,
}
impl<D: CryptoDevice> SimpleCrypto<D> 
{
    fn new(device: D) -> Self {
        Self {
            device
        }
    }
}
impl<D: CryptoDevice> Crypto<D> for SimpleCrypto<D> 
{
    fn device(&self) -> &D {
        & self.device
    }
    fn device_mut(&mut self) -> &mut D {
        &mut self.device
    }

    fn encrypt(&mut self, key: &[u8], data: &[u8]) -> Result<Vec<u8>>{
        if !self.device().is_open() {
            self.device_mut().open()?;
        }
        self.device().encrypt(key, data)
        //Crypto::encrypt(self, key, data)
    }
}




#[cfg(test)]
mod test {
    use super::*;
    use std::io::Result;
    #[test]
    fn test_main() -> Result<()>{
        let mut crypto = SimpleCrypto::new(XYDDevice::default());
        let out = crypto.encrypt(&[0_u8, 1_u8, 2_u8, 3_u8], &[10_u8, 11_u8, 12_u8, 13_u8])?;
        println!("encrypted data:{:?}", out);
        Ok(())
    }
}